package com.gitee.easyopen;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.ValidatorFactory;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import com.gitee.easyopen.exception.ApiException;
import com.gitee.easyopen.exception.ErrorSignException;
import com.gitee.easyopen.message.ErrorFactory;
import com.gitee.easyopen.message.Errors;
import com.gitee.easyopen.util.ApiUtil;

/**
 * 负责校验,校验工作都在这里
 * 
 * @author tanghc
 *
 */
public class ApiValidator implements Validator {
    private static final Logger logger = LoggerFactory.getLogger(ApiValidator.class);

    private static List<String> FORMAT_LIST = Arrays.asList("json", "xml");
    private static Object[] EMPTY_OBJ_ARRAY = {};

    private static ValidatorFactory factory;
    private static javax.validation.Validator validator;
    static {
        factory = Validation.buildDefaultValidatorFactory();
        validator = factory.getValidator();
    }

    private ApiConfig apiConfig;

    public ApiValidator() {
        super();
    }

    public ApiValidator(ApiConfig apiConfig) {
        super();
        this.apiConfig = apiConfig;
    }

    @Override
    public void validate(ApiParam param) throws Throwable {
        if (apiConfig.isIgnoreValidate() || param.fatchIgnoreValidate()) {
            logger.debug("忽略所有验证(ignoreValidate=true)");
            return;
        }
        Assert.notNull(apiConfig.getAppSecretManager(), "appSecretManager未初始化");
        // 需要验证签名
        if (param.fatchIgnoreSign()) {
            logger.debug("忽略签名验证(ignoreSign=true)");
        } else {
            checkAppKey(param);
            checkSign(param);
        }
        checkTimeout(param);
        checkFormat(param);
    }

    protected void checkTimeout(ApiParam param) {
        int timeoutSeconds = apiConfig.getTimeoutSeconds();
        if (timeoutSeconds <= 0) {
            throw new IllegalArgumentException("timeoutSeconds参数不能小于0");
        }
        String requestTime = param.fatchTimestamp();
        try {
            Date requestDate = new SimpleDateFormat(ParamNames.TIMESTAMP_PATTERN).parse(requestTime);
            long requestMilliseconds = requestDate.getTime();
            if (System.currentTimeMillis() - requestMilliseconds > timeoutSeconds * 1000) {
                throw Errors.TIMEOUT.getException(param.fatchNameVersion(), timeoutSeconds);
            }
        } catch (ParseException e) {
            throw Errors.TIME_INVALID.getException(param.fatchNameVersion());
        }
    }

    protected void checkAppKey(ApiParam param) {
        Assert.notNull(apiConfig.getAppSecretManager(), "appSecretManager未初始化");
        if (StringUtils.isEmpty(param.fatchAppKey())) {
            throw Errors.NO_APP_ID.getException(param.fatchNameVersion(), ParamNames.APP_KEY_NAME);
        }
        boolean isTrueAppKey = apiConfig.getAppSecretManager().isValidAppKey(param.fatchAppKey());
        if (!isTrueAppKey) {
            throw Errors.ERROR_APP_ID.getException(param.fatchNameVersion(), ParamNames.APP_KEY_NAME);
        }
    }

    protected void checkSign(ApiParam param) {
        if (StringUtils.isEmpty(param.fatchSign())) {
            throw Errors.NO_SIGN_PARAM.getException(param.fatchNameVersion(), ParamNames.SIGN_NAME);
        }
        String secret = apiConfig.getAppSecretManager().getSecret(param.fatchAppKey());
        try {
            ApiUtil.checkSign(param, secret);
        } catch (ErrorSignException e) {
            throw Errors.ERROR_SIGN.getException(param.fatchNameVersion());
        }
    }

    protected void checkFormat(ApiParam param) {
        String format = param.fatchFormat();
        if (StringUtils.isEmpty(format)) {
            return;
        }
        boolean contains = FORMAT_LIST.contains(format.toLowerCase());

        if (!contains) {
            throw Errors.NO_FORMATTER.getException(param.fatchNameVersion(), format);
        }
    }

    @Override
    public void validateBusiParam(Object obj) {
        Set<ConstraintViolation<Object>> set = validator.validate(obj);

        for (ConstraintViolation<Object> c : set) {
            String errorMsg = c.getMessage();
            throw this.getValidateBusiParamException(errorMsg);
        }
    }

    private RuntimeException getValidateBusiParamException(String errorMsg) {
        String code = Errors.BUSI_PARAM_ERROR.getCode();
        String[] msgToken = errorMsg.split("=");
        String msg = msgToken[0];
        if (msg.startsWith("{") && msg.endsWith("}")) {
            String module = msg.substring(1, msg.length() - 1);
            Object[] params = this.buildParams(msgToken);
            String error = ErrorFactory.getErrorMessage(module, ApiContext.getLocal(), params);
            return new ApiException(error, code);
        } else {
            return new ApiException(errorMsg, code);
        }
    }

    private Object[] buildParams(String[] msgToken) {
        if (msgToken.length == 2) {
            return msgToken[1].split(",");
        } else {
            return EMPTY_OBJ_ARRAY;
        }
    }

    @Override
    public void setApiConfig(ApiConfig apiConfig) {
        this.apiConfig = apiConfig;
    }

    @Override
    public ApiConfig getApiConfig() {
        return apiConfig;
    }

}
